package futbol.tacticas.concursantes.FCThePatetics;

import java.util.ArrayList;
import static java.lang.Math.*;

/**Calculos varios*/
public class Calc {

    public void printAngulos(double[][] ang) {
        boolean first = true;
        for (double[] a : ang) {
            if (!first) {
                //System.out.print(" , ");
            }
            //System.out.println((int) (a[0] * 180 / PI) + "->" + (int) (a[1] * 180 / PI) + "");
            first = false;
        }
    }

    /**Permite controlar un entero entre un minimo y un maximo*/
    public int control(int d, int min, int max) {
        if (d < min) {
            return min;
        }
        if (d > max) {
            return max;
        }
        return d;
    }

    /**Permite controlar un double entre un minimo y un maximo*/
    public double control(double d, double min, double max) {
        if (d < min) {
            return min;
        }
        if (d > max) {
            return max;
        }
        return d;
    }

    /**Retorna el cuadrado de binomio, si el resultado es complejo retorna null*/
    public double[] cuadBinomio(double a, double b, double c) {
        double[] d = new double[2];
        if (b * b - 4 * a * c >= 0) {
            double sqrt = sqrt(b * b - 4 * a * c);
            d[0] = (-b + sqrt) / (2 * a);
            d[1] = (-b - sqrt) / (2 * a);
            return d;
        } else {
            return null;
        }
    }

    /**Retorna el angulo entre dos puntos, el rango es [-Pi,Pi]*/
    public double angulo(double x0, double y0, double x1, double y1) {
        return angulo(x1 - x0, y1 - y0);
    }

    /**Retorna el angulo para una coordenada, el rango es [-Pi,Pi]*/
    public double angulo(double dx, double dy) {
        double angle = 0;
        if (dx > 0) {
            angle = atan(dy / dx);
        } else if (dx < 0) {
            angle = atan(dy / dx) + PI;
        } else if (dy < 0) {
            angle = -PI / 2;
        } else {
            angle = PI / 2;
        }
        angle = angle % (PI * 2d);
        if (angle >= PI) {
            angle = angle - 2d * PI;
        }
        return angle;
    }

    /**Retorna la distancia entre dos puntos*/
    public double distancia(double x1, double y1, double x0, double y0) {
        return distancia(x1 - x0, y1 - y0);
    }

    /**Retorna la distancia de una coordenada*/
    public double distancia(double dx, double dy) {
        return sqrt(dx * dx + dy * dy);
    }

    /**Retorna los angulos delimitantes de una circunferencia dentro de la cancha*/
    private double[][] limitarCancha(double x0, double y0, double r0) {
        double[][] limites = new double[][]{{-PI, PI}};
        double ang1, ang2;
        double[] dd1 = cuadBinomio(1, -2 * x0, x0 * x0 + 14400 - 240 * y0 + y0 * y0 - r0 * r0);//y=120                
        if (dd1 != null) {
            ang1 = angulo(x0, y0, dd1[0], 120);
            ang2 = angulo(x0, y0, dd1[1], 120);
            limites = cortarAngulos(limites, new double[]{ang1, ang2});
        }
        double[] dd3 = cuadBinomio(1, -2 * y0, x0 * x0 + 25600 - 320 * x0 + y0 * y0 - r0 * r0);//x=160
        if (dd3 != null) {
            ang1 = angulo(x0, y0, 160, dd3[1]);
            ang2 = angulo(x0, y0, 160, dd3[0]);
            limites = cortarAngulos(limites, new double[]{ang1, ang2});
        }
        double[] dd0 = cuadBinomio(1, -2 * x0, x0 * x0 + 14400 + 240 * y0 + y0 * y0 - r0 * r0);//y=-120                
        if (dd0 != null) {
            ang1 = angulo(x0, y0, dd0[1], -120);
            ang2 = angulo(x0, y0, dd0[0], -120);
            limites = cortarAngulos(limites, new double[]{ang1, ang2});
        }
        double[] dd2 = cuadBinomio(1, -2 * y0, x0 * x0 + 25600 + 320 * x0 + y0 * y0 - r0 * r0);//x=-160
        if (dd2 != null) {
            ang1 = angulo(x0, y0, -160, dd2[0]);
            ang2 = angulo(x0, y0, -160, dd2[1]);
            limites = cortarAngulos(limites, new double[]{ang1, ang2});
        }
        return limites;
    }

    /**Retorna los angulos complementos de un conjunto de angulos*/
    public double[][] complementoAngulos(double[][] angulos) {
        return cortarAngulos(new double[][]{{-PI, PI}}, angulos);
    }

    /**A un conjunto de angulos les resta otro conjunto de angulos*/
    public double[][] cortarAngulos(double[][] angulos, double[][] cortes) {
        for (double[] corte : cortes) {
            angulos = cortarAngulos(angulos, corte);
        }
        return angulos;
    }

    /**A un conjunto de angulos le resta una seccion de angulos*/
    public double[][] cortarAngulos(double[][] angulos, double[] corte) {
        if (corte[1] < corte[0]) {
            double[] c1 = new double[]{corte[0], PI};
            double[] c2 = new double[]{-PI, corte[1]};
            return cortarAngulos(cortarAngulos(angulos, c1), c2);
        }
        ArrayList<double[]> ang = new ArrayList<double[]>();
        for (int i = 0; i < angulos.length; i++) {
            boolean in = false;
            if (corte[0] > angulos[i][0] && corte[0] < angulos[i][1]) {
                ang.add(new double[]{angulos[i][0], corte[0]});
                in = true;
            }
            if (corte[1] > angulos[i][0] && corte[1] < angulos[i][1]) {
                ang.add(new double[]{corte[1], angulos[i][1]});
                in = true;
            }
            if (!in && !(corte[0] <= angulos[i][0] && corte[1] >= angulos[i][1])) {
                ang.add(new double[]{angulos[i][0], angulos[i][1]});
            }
        }

        double[][] angs = new double[ang.size()][2];
        ang.toArray(angs);
        return angs;
    }

    /**Retorna los angulos de la primera circunferencia que no estan dentro de la segunda circunferencia*/
    public double[][] restarCircunferencia(double x0, double y0, double r0, double x1, double y1, double r1) {
        double[][] r = interseccionCircunferencias(x0, y0, r0, x1, y1, r1);
        double[][] d;
        if (r == null) {            
            double dist = distancia(x0, y0, x1, y1);            
            if (dist > r1-r0) {
                d = new double[][]{{-PI, PI}};
            } else {
                d = new double[][]{};
            }
        } else {            
            if (Double.isNaN(r[0][0]) || Double.isNaN(r[0][1]) || Double.isNaN(r[1][0]) || Double.isNaN(r[1][1])) {
                
                    d = new double[][]{};         
                
            } else {
                double ang1 = angulo(x0, y0, r[0][0], r[0][1]);
                double ang0 = angulo(x0, y0, r[1][0], r[1][1]);
                if (x0 == x1) {
                    double ang2 = ang1;
                    ang1 = ang0;
                    ang0 = ang2;
                }
                if (ang0 < ang1) {
                    d = new double[][]{{ang0, ang1}};
                } else {
                    if (ang1 == -PI) {
                        d = new double[][]{{ang0, PI}};
                    } else if (ang0 == PI) {
                        d = new double[][]{{-PI, ang1}};
                    } else {
                        d = new double[][]{{-PI, ang1}, {ang0, PI}};
                    }
                }
            }
        }
        //d = interseccionAngulos(d,limitarCancha(x0,y0,r0));
        return d;
    }

    /**Retorna la interseccion entre dos conjunto de secciones de angulos*/
    public double[][] interseccionAngulos(double[][] d1, double[][] d2) {
        ArrayList<double[]> list = new ArrayList<double[]>();
        double ang1, ang2;
        if (d1.length == 0 && d2.length == 0) {
            return new double[][]{};
        }
        for (double[] corte : d1) {
            for (double[] angulo : d2) {
                if (corte[0] <= angulo[1] && corte[1] >= angulo[0]) {
                    ang1 = max(corte[0], angulo[0]);
                    ang2 = min(corte[1], angulo[1]);
                    if (ang1 != ang2) {
                        list.add(new double[]{ang1, ang2});
                    }
                }
            }
        }
        double[][] r = new double[list.size()][2];
        list.toArray(r);
        return r;
    }

    /**Retorna los puntos de corte entre dos curcunferencias*/
    private double[][] interseccionCircunferencias(double x0, double y0, double r0, double x1, double y1, double r1) {
        if (x0 == x1 && y0 == y1) {
            if (r0 <= r1) {
                return new double[][]{{Double.NaN, Double.NaN}, {Double.NaN, Double.NaN}};
            } else {
                return null;
            }
        } else if (x0 == x1 && y0 != y1) {
            double[][] r = interseccionCircunferencias(y0, x0, r0, y1, x1, r1);
            double[][] rr = new double[2][2];
            if (r != null) {
                rr[0][0] = r[0][1];
                rr[0][1] = r[0][0];
                rr[1][0] = r[1][1];
                rr[1][1] = r[1][0];
            } else {
                rr = null;
            }
            return rr;
        } else {
            double a, b, c, d3, d4, d5, d6, d7, d8, d9, da;
            d3 = -2 * x0;
            d4 = -2 * y0;
            d5 = x0 * x0 + y0 * y0 - r0 * r0;
            d6 = -2 * x1;
            d7 = -2 * y1;
            d8 = x1 * x1 + y1 * y1 - r1 * r1;
            d9 = (d7 - d4) / (d3 - d6);
            da = (d8 - d5) / (d3 - d6);
            a = d9 * d9 + 1;
            b = 2 * d9 * da + d3 * d9 + d4;
            c = da * da + da * d3 + d5;
            double[][] r = new double[2][2];
            double[] cuad = cuadBinomio(a, b, c);
            if (cuad == null) {
                if (distancia(x0, y0, x1, y1) < r1 && r0 <= r1) {
                    return new double[][]{{Double.NaN, Double.NaN}, {Double.NaN, Double.NaN}};
                }
                return null;
            }
            r[0][1] = cuad[0];
            r[1][1] = cuad[1];
            r[0][0] = ((x1 * x1 - x0 * x0 + y1 * y1 - y0 * y0 + r0 * r0 - r1 * r1) + 2 * (y0 - y1) * r[0][1]) / (2 * (x1 - x0));
            r[1][0] = ((x1 * x1 - x0 * x0 + y1 * y1 - y0 * y0 + r0 * r0 - r1 * r1) + 2 * (y0 - y1) * r[1][1]) / (2 * (x1 - x0));
            double ang = angulo(x0, y0, x1, y1);
            if (ang <= PI / 2 && ang >= -PI / 2) {
                double[] d = new double[]{r[0][0], r[0][1]};
                r[0][0] = r[1][0];
                r[0][1] = r[1][1];
                r[1][0] = d[0];
                r[1][1] = d[1];
            }
            return r;
        }
    }

    /**Reduce el ancho de las secciones de angulos en 'error' al inicio y final*/
    public double[][] eliminarError(double[][] angulos, double error) {
        ArrayList<double[]> list = new ArrayList<double[]>();
        for (double[] a : angulos) {
            if (a[0] > -PI) {
                a[0] = a[0] + error;
            }
            if (a[1] < PI) {
                a[1] = a[1] - error;
            }
            if (a[0] < a[1]) {
                list.add(a);
            }
        }
        double[][] d = new double[list.size()][2];
        list.toArray(d);
        return d;
    }
}
